package com.hk.commons.util;

import lombok.SneakyThrows;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.core.io.UrlResource;

import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * String 相关工具类
 *
 * @author kevin
 * @date 2017年8月30日下午1:15:35
 */
public abstract class StringUtils {

    /**
     * 小数点符号
     */
    public static final String DOG = ".";

    /**
     * - 分隔符
     */
    public static final String RUNG = "-";

    /**
     * : 分隔符
     */
    public static final String COLON_SEPARATE = ":";

    /**
     * , 分隔符
     */
    public static final String COMMA_SEPARATE = ",";

    /**
     * 空字符串
     */
    public static final String EMPTY = org.apache.commons.lang3.StringUtils.EMPTY;
    /**
     * 换行符
     */
    public static final String LF = org.apache.commons.lang3.StringUtils.LF;
    /**
     * 空白字符
     */
    public static final String SPACE = org.apache.commons.lang3.StringUtils.SPACE;
    /**
     * 下划线 pattern
     */
    private static final Pattern LINE_PATTERN = Pattern.compile("_(\\w)");
    /**
     * 数字开头匹配
     */
    private static final Pattern NUMBER_PATTERN = Pattern.compile("[0-9]*");
    /**
     * 字母开头匹配
     */
    private static final Pattern LETTER_PATTERN = Pattern.compile("[A-Z]*");
    /**
     * 大写字符匹配
     */
    private static final String HUMP_TO_LINE = "[A-Z]";
    /**
     * ${xxx} ，其中 xxx 必须为 匹配字母或数字或下划线或数字。等价于 '[^A-Za-z0-9_]'
     */
    private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{\\w+}");

    private static final char[] LETTER = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
            'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};


    private StringUtils(){

    }

    /**
     * 根据参数值 value 生成由 prefix-字母-数字 三段组成的字符串，字母从 {@link #LETTER}中产生
     *
     * @param value       当前值，从0开始，自增
     * @param charLength  生成的字符位数，
     *                    如3，则生成 AAA ~ ZZZ ，共有 26个字母三位任意组合，组合结果有 26*26*26个，
     *                    当value的值超过 26*26*26*numMaxValue 值时，生成的值会从头开始。
     *                    如果为4，则生成 AAAA ~ ZZZZ，共有26个字母四位任意组合，组合结果有 26*26*26*26个，
     *                    当value的值超过 26*26*26*26*numMaxValue 值时，生成的值会从头开始。
     *                    依次类推。
     * @param numMaxValue 生成的数字最大个数，如1000，则生成的数字段有 (0~999) 1000个数
     * @return prefix-AAA-001 这种格式的值，其中 AAA的范围为 AAA ~ ZZZ ，(根据digit的值生成指定的位数) 000的范围为 000 ~ 999。(根据 maxNum指定生成的位数)
     */
    public static String toLetterString(String prefix, int value, int charLength, int numMaxValue) {
        return toLetterString(prefix, value, charLength, numMaxValue, LETTER);
    }

    /**
     * 根据参数值 value 生成由 prefix-字母-数字 三段组成的字符串
     *
     * @param value       当前值，从0开始，自增
     * @param charLength  生成的字符位数，
     *                    如3，则生成 AAA ~ ZZZ ，共有 26个字母三位任意组合，组合结果有 26*26*26个，
     *                    当value的值超过 26*26*26*numMaxValue 值时，生成的值会从头开始。
     *                    如果为4，则生成 AAAA ~ ZZZZ，共有26个字母四位任意组合，组合结果有 26*26*26*26个，
     *                    当value的值超过 26*26*26*26*numMaxValue 值时，生成的值会从头开始。
     *                    依次类推。
     * @param numMaxValue 生成的数字最大个数，如1000，则生成的数字段有 (0~999) 1000个数
     * @param chars       字母段从哪些字符中生成
     * @return prefix-AAA-001 这种格式的值，其中 AAA的范围为 AAA ~ ZZZ ，(根据digit的值生成指定的位数) 000的范围为 000 ~ 999。(根据 maxNum指定生成的位数)
     */
    public static String toLetterString(String prefix, int value, int charLength, int numMaxValue, char[] chars) {
        AssertUtils.isTrue(value >= 0, "value must be gte zero.");
        int charsLen = chars.length;
        AssertUtils.isTrue(charsLen > 0, "chars length must be gte zero.");
        AssertUtils.isTrue(charLength <= charsLen, "digit must be lt charLen.");
        char[] letterChars = new char[charLength];
        // 使用循环i 自增方式
//        for (int i = 0; i < charLength; i++) {
//            chars[charLength - i - 1] = arr[(value / ((int) Math.pow(charsLen, i) * maxNum)) % charsLen];
//        }
//      使用循环 i 自减方式
        for (int i = charLength; i > 0; ) {
            int mod = (int) Math.pow(charsLen, (double) charLength - i);
            int index = (value / (mod * numMaxValue)) % charsLen;
            letterChars[--i] = chars[index];
        }

        // 固定方式
//        int x = (value / maxNum) % charsLen;
//        int y = (value / (26 * maxNum)) % charsLen;
//        int z = (value / (26 * 26 * maxNum)) % charsLen;
//        return new String(chars) + "-" + String.format("%03d", value % maxNum);
        return String.format(STR."%s-%s-%0\{charLength}d", prefix, new String(letterChars), (value % numMaxValue));
    }


    /**
     * 模板处理:
     * <pre>
     * 你好，你的验证码为 ${code},过期时间为 ${expire} 分钟
     * 返回:  你好，你的验证码为 params.get("code"),过期时间为 params.get("expire") 分钟
     * 注意: template 中的 变量必须是以 ${变量名} ，变量名正则 为 \\w {@link #VARIABLE_PATTERN}
     * </pre>
     *
     * @param template template
     * @param params   params
     * @return template
     */
    public static String processTemplate(String template, Map<String, ?> params) {
        if (CollectionUtils.isEmpty(params)) {
            return template;
        }
        var sb = new StringBuilder();
        var m = VARIABLE_PATTERN.matcher(template);
        while (m.find()) {
            var param = m.group();
            var value = params.get(param.substring(2, param.length() - 1));// 截取 ${  与  }
            m.appendReplacement(sb, Objects.isNull(value) ? EMPTY : value.toString());
        }
        m.appendTail(sb);
        return sb.toString();
    }

    /**
     * 下划线转小驼峰
     *
     * @param str str
     * @return String
     */
    public static String lineToSmallHump(String str) {
        return lineToHump(uncapitalize(str));
    }

    public static String uncapitalize(String str) {
        return org.springframework.util.StringUtils.uncapitalize(str);
    }

    private static String lineToHump(String str) {
        var matcher = LINE_PATTERN.matcher(str);
        var sb = new StringBuilder();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * Unicode to UTF-8
     */
    public static String unicodeToUTF8(String unicode) {
        return StringEscapeUtils.unescapeJava(unicode);
    }

    /**
     * 判断是否以数字开头
     *
     * @param str str
     * @return true or false
     */
    public static boolean startWithNumber(String str) {
        return isNotEmpty(str) && NUMBER_PATTERN.matcher(String.valueOf(str.charAt(0))).matches();
    }

    /**
     * 判断是否以字母开头
     *
     * @param str str
     * @return true or false
     */
    public static boolean startWithLetter(String str) {
        return isNotEmpty(str) && LETTER_PATTERN.matcher(String.valueOf(str.charAt(0))).matches();
    }

    /**
     * 下划线转大驼峰
     *
     * @param str str
     */
    public static String lineToBigHump(String str) {
        return lineToHump(capitalize(str));
    }

    public static String capitalize(String str) {
        return org.springframework.util.StringUtils.capitalize(str);
    }

    /**
     * 驼峰转下划线小写
     *
     * @param str str
     * @return {@link String}
     */
    public static String humpToLineLower(String str) {
        return StringUtils.isEmpty(str) ? null : str.replaceAll(HUMP_TO_LINE, "_$0").toLowerCase();
    }

    /**
     * 驼峰转下划线大写
     *
     * @param str str
     * @return {@link String}
     */
    public static String humpToLineUpper(String str) {
        return StringUtils.isEmpty(str) ? null : str.replaceAll(HUMP_TO_LINE, "_$0").toUpperCase();
    }

    /**
     * 与任意字符串比较，如果只要有一个相同的，返回 true,否则返回false
     *
     * @param cs1           cs1
     * @param charSequences charSequences
     * @return true or false
     */
    public static boolean equalsAny(CharSequence cs1, CharSequence... charSequences) {
        return org.apache.commons.lang3.StringUtils.equalsAny(cs1, charSequences);
    }

    /**
     * string to byte[] ，如果参数 s 为 null or "" ，返回 null
     *
     * @param s s
     * @return byte[]
     */
    public static byte[] getByteUtf8(String s) {
        return isEmpty(s) ? null : s.getBytes(StandardCharsets.UTF_8);
    }

    /**
     * 是否为数字类型
     *
     * @param args args
     * @return true or false
     */
    public static boolean isNumber(String args) {
        return NumberUtils.isCreatable(args);
    }

    /**
     * @param bytes bytes
     * @return String
     */
    public static String newStringIso8859_1(byte[] bytes) {
        return new String(bytes, StandardCharsets.ISO_8859_1);
    }

    /**
     * 与任意字符串忽略大小写比较，如果只要有一个相同的，返回 true,否则返回false
     *
     * @param cs1           cs1
     * @param charSequences charSequences
     * @return true or false
     */
    public static boolean equalsAnyIgnoreCase(CharSequence cs1, CharSequence... charSequences) {
        return org.apache.commons.lang3.StringUtils.equalsAnyIgnoreCase(cs1, charSequences);
    }

    /**
     * <pre>
     *
     * 如果参数  str 为 null ，返回 defaultStr
     * 如果参数  str 为 "" ，返回 defaultStr
     * 如果参数  str 为 "   " ，返回 defaultStr
     * </pre>
     *
     * @param str        str
     * @param defaultStr defaultStr
     * @param <T>        T
     * @return T
     */
    public static <T extends CharSequence> T defaultIfBlank(final T str, final T defaultStr) {
        return isBlank(str) ? defaultStr : str;
    }

    /**
     * <pre>
     *
     * 如果参数  str 为 null ，返回 defaultStr
     * 如果参数  str 为 "" ，返回 defaultStr
     * </pre>
     *
     * @param str        str
     * @param defaultStr defaultStr
     * @param <T>        T
     * @return T
     */
    public static <T extends CharSequence> T defaultIfEmpty(final T str, final T defaultStr) {
        return isEmpty(str) ? defaultStr : str;
    }

    /**
     * <p>
     * 判断是否不为 null or ""
     * </p>
     * <p>
     *
     * <pre>
     * StringUtils.isNotEmpty(null)      = false
     * StringUtils.isNotEmpty("")        = false
     * StringUtils.isNotEmpty("ad")      = true
     * StringUtils.isNotEmpty(" ad ")    = true
     * </pre>
     *
     * @param str str
     * @return true or false
     */
    public static boolean isNotEmpty(CharSequence str) {
        return hasLength(str);
    }

    public static boolean hasLength(CharSequence str) {
        return org.springframework.util.StringUtils.hasLength(str);
    }

    public static boolean isEmpty(CharSequence str) {
        return !hasLength(str);
    }

    /**
     * <pre>
     * StringUtils.isBlank(null) = true
     * StringUtils.isBlank("") = true
     * StringUtils.isBlank(" ") = true
     * StringUtils.isBlank("bob") = false
     * StringUtils.isBlank(" bob ") = false
     *
     * </pre>
     *
     * @param args args
     * @return true or false
     */
    public static boolean isBlank(CharSequence args) {
        return org.apache.commons.lang3.StringUtils.isBlank(args);
    }

    /**
     * <pre>
     * StringUtils.isNotBlank(null) = false
     * StringUtils.isNotBlank("") = false
     * StringUtils.isNotBlank(" ") = false
     * StringUtils.isNotBlank("bob") = true
     * StringUtils.isNotBlank(" bob ") = true
     *
     * </pre>
     *
     * @param args args
     * @return true or false
     */
    public static boolean isNotBlank(CharSequence args) {
        return !isBlank(args);
    }

    /**
     * 根据英文逗号切割字符串
     *
     * @param args 要切割的字符串
     * @return 切割后的数组
     */
    public static String[] splitByComma(String args) {
        return tokenizeToStringArray(args, COMMA_SEPARATE);
    }

    public static String[] tokenizeToStringArray(String str, String delimiters) {
        return org.springframework.util.StringUtils.tokenizeToStringArray(str, delimiters);
    }

    public static Set<String> commaDelimitedListToSet(String str) {
        return org.springframework.util.StringUtils.commaDelimitedListToSet(str);
    }

    public static String collectionToDelimitedString(Collection<?> coll, String delim) {
        return org.springframework.util.StringUtils.collectionToDelimitedString(coll, delim);
    }

    public static String replace(String str, String oldPattern, String newPattern) {
        return org.springframework.util.StringUtils.replace(str, oldPattern, newPattern);
    }

    public static boolean endsWithIgnoreCase(String str, String suffix) {
        return org.springframework.util.StringUtils.endsWithIgnoreCase(str, suffix);
    }

    public static boolean hasText(String str) {
        return org.springframework.util.StringUtils.hasText(str);
    }

    public static String delete(String str, String pattern) {
        return org.springframework.util.StringUtils.delete(str, pattern);
    }

    public static String arrayToCommaDelimitedString(Object[] arr) {
        return org.springframework.util.StringUtils.arrayToCommaDelimitedString(arr);
    }

    public static boolean startsWithIgnoreCase(String str, String prefix) {
        return org.springframework.util.StringUtils.startsWithIgnoreCase(str, prefix);
    }

    public static <T> List<T> splitByComma(String args, Class<T> clazz) {
        return Arrays.stream(splitByComma(args)).map(item -> ConverterUtils.defaultConvert(item, clazz)).collect(Collectors.toList());
    }


    /**
     * 忽略大小写比较是否相等
     *
     * @param cs1 cs1
     * @param cs2 cs2
     * @return true or false
     */
    public static boolean equalsIgnoreCase(CharSequence cs1, CharSequence cs2) {
        return org.apache.commons.lang3.StringUtils.equalsIgnoreCase(cs1, cs2);
    }

    /**
     * 查询 searchSeq 在 seq 中的索引位置
     *
     * @param seq       seq
     * @param searchSeq searchSeq
     * @return 索引位置，不存在返回 -1
     */
    public static int indexOf(final CharSequence seq, final CharSequence searchSeq) {
        return org.apache.commons.lang3.StringUtils.indexOf(seq, searchSeq);
    }

    /**
     * 判断两个字符串是否不相等
     *
     * @param cs1 cs1
     * @param cs2 cs2
     * @return true or false
     */
    public static boolean notEquals(CharSequence cs1, CharSequence cs2) {
        return !equals(cs1, cs2);
    }

    /**
     * 手机号中间四位打码 : 18888888888  -> 188****8888
     *
     * @param phone 手机号
     * @return 打码后的手机号
     */
    public static String phoneMiddleMosaic(String phone) {
        return ValidateUtils.isMobilePhone(phone) ? String.format("%s****%s", phone.substring(0, 3),
                phone.substring(7, 11)) : phone;
    }

    /**
     * 手机号最后四位打码 : 18888888888  -> 1888888****
     *
     * @param phone 手机号
     * @return 打码后的手机号
     */
    public static String phoneLastMosaic(String phone) {
        return ValidateUtils.isMobilePhone(phone) ? STR."\{phone.substring(0, 7)}****" : phone;
    }

    /**
     * 相等
     *
     * @param cs1 cs1
     * @param cs2 cs2
     * @return boolean
     */
    public static boolean equals(CharSequence cs1, CharSequence cs2) {
        return org.apache.commons.lang3.StringUtils.equals(cs1, cs2);
    }

    /**
     * 是否以 suffix 结尾
     *
     * @param str    str
     * @param suffix suffix
     * @return true or false
     */
    public static boolean endsWith(CharSequence str, CharSequence suffix) {
        return org.apache.commons.lang3.StringUtils.endsWith(str, suffix);
    }

    /**
     * 是否包含指定字符串 contains
     *
     * @param seq       seq
     * @param searchSeq searchSeq
     * @return true or false
     */
    public static boolean contains(CharSequence seq, CharSequence searchSeq) {
        return org.apache.commons.lang3.StringUtils.contains(seq, searchSeq);
    }

    /**
     * 是否包含指定字符串 contains
     *
     * @param seq       seq
     * @param searchSeq searchSeq
     * @return true or false
     */
    public static boolean containsIgnoreCase(CharSequence seq, CharSequence searchSeq) {
        return org.apache.commons.lang3.StringUtils.containsIgnoreCase(seq, searchSeq);
    }

    /**
     * 是否以 searchSequence 中任意一个开始
     *
     * @param sequence       sequence
     * @param searchSequence searchSequence
     * @return true or false
     */
    public static boolean startsWithAny(CharSequence sequence, CharSequence... searchSequence) {
        return org.apache.commons.lang3.StringUtils.startsWithAny(sequence, searchSequence);
    }

    /**
     * 删除指定的字符串
     *
     * @param str    str
     * @param remove 要删除的字符串
     * @since 2.6.5
     */
    public static String remove(String str, String remove) {
        return org.apache.commons.lang3.StringUtils.remove(str, remove);
    }

    /**
     * 判断 str 是否以  prefix 开头
     *
     * @param str    str
     * @param prefix prefix
     * @return true or false
     */
    public static boolean startsWith(CharSequence str, CharSequence prefix) {
        return org.apache.commons.lang3.StringUtils.startsWith(str, prefix);
    }

    /**
     * 截取字符串
     *
     * @param str   str
     * @param start start
     * @return {@link String}
     */
    public static String substring(String str, int start) {
        return org.apache.commons.lang3.StringUtils.substring(str, start);
    }

    /**
     * 截取字符串
     *
     * @param str   str
     * @param start start
     * @param end   end
     * @return {@link String}
     */
    public static String substring(String str, int start, int end) {
        return org.apache.commons.lang3.StringUtils.substring(str, start, end);
    }

    /**
     * 截取之前部分
     *
     * @param str       str
     * @param separator separator
     * @return {@link String}
     */
    public static String substringBefore(String str, String separator) {
        return org.apache.commons.lang3.StringUtils.substringBefore(str, separator);
    }

    /**
     * 根据查询的最后字符串截取之前部分
     *
     * @param str       str
     * @param separator separator
     * @return {@link String}
     */
    public static String substringBeforeLast(String str, String separator) {
        return org.apache.commons.lang3.StringUtils.substringBeforeLast(str, separator);
    }

    /**
     * 截取之后部分
     *
     * @param str       str
     * @param separator separator
     * @return {@link String}
     */
    public static String substringAfter(String str, String separator) {
        return org.apache.commons.lang3.StringUtils.substringAfter(str, separator);
    }

    /**
     * 根据查询的最后字符串截取之后部分
     *
     * @param str       str
     * @param separator separator
     * @return {@link String}
     */
    public static String substringAfterLast(String str, String separator) {
        return org.apache.commons.lang3.StringUtils.substringAfterLast(str, separator);
    }

    /**
     * trim To Null
     *
     * @param str str
     * @return {@link String}
     */
    public static String trimToNull(String str) {
        return org.apache.commons.lang3.StringUtils.trimToNull(str);
    }

    /**
     * trim To Empty
     *
     * @param str str
     * @return {@link String}
     */
    public static String trimToEmpty(String str) {
        return org.apache.commons.lang3.StringUtils.trimToEmpty(str);
    }

    /**
     * String url 转换成 {@link UrlResource}
     *
     * @param url url
     * @return {@link UrlResource}
     */
    @SneakyThrows(value = {MalformedURLException.class})
    public static UrlResource createResource(String url) {
        return new UrlResource(url);
    }

}
